package cn.wolfcode.shop.web.controller;

import cn.wolfcode.shop.anno.UserParam;
import cn.wolfcode.shop.common.BusinessException;
import cn.wolfcode.shop.common.Result;
import cn.wolfcode.shop.consts.MQConstents;
import cn.wolfcode.shop.domain.OrderInfo;
import cn.wolfcode.shop.domain.User;
import cn.wolfcode.shop.mq.msg.CreateOrderMsg;
import cn.wolfcode.shop.msg.SeckillServerCodeMsg;
import cn.wolfcode.shop.service.IOrderInfoService;
import cn.wolfcode.shop.service.ISeckillGoodService;
import cn.wolfcode.shop.service.ISeckillOrderService;
import cn.wolfcode.shop.util.RedisKeys;
import cn.wolfcode.shop.vo.SeckillGoodVO;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

@RestController
@RequestMapping("/api/orders")
public class OrderInfoController {
    @Autowired
    private ISeckillGoodService seckillGoodService;
    @Autowired
    private ISeckillOrderService seckillOrderService;
    @Autowired
    private IOrderInfoService orderInfoService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    // 创建 ConcurrentHashMap 做本地缓存，用static修饰的原因，存在线程之间共享区域（方法区），无并发问题
    public static final ConcurrentHashMap<Long, Boolean> STOCK_COUNT_STATE_MAP = new ConcurrentHashMap<>();


    @RequestMapping("/findById")
    public Result<OrderInfo> findById(String orderNo, @UserParam User user) {
        // 安全问题：只有自己才可以查自己的订单信息，所以需要加上用户信息去查询
        OrderInfo orderInfo = orderInfoService.findByIdAndUserId(orderNo, user.getId());
        return Result.success(orderInfo);
    }

    // 秒杀操作
    //@RequestMapping("/doSeckill")
    @RequestMapping("/{random}/doSeckill")
    public Result<String> doSeckill(Long seckillId, String uuid,
                                    @UserParam User user,
                                    @PathVariable("random") String random) {
        // 校验逻辑
        if (user == null || seckillId == null || StringUtils.isEmpty(uuid) || StringUtils.isEmpty(random)) {
            throw new BusinessException(SeckillServerCodeMsg.OPS_ERROR);
        }

        // 去Redis中查存好的随机数，和参数做校验
        String randomInRedis = redisTemplate.opsForValue().get(RedisKeys.RANDOM_IN_PATH.join(
                user.getId() + "", seckillId + ""));
        // 对路径中用来隐藏地址的随机数做校验
        if (StringUtils.isEmpty(randomInRedis) || !random.equals(randomInRedis)) {
            // 随机数不相同，或者不存在都抛异常
            throw new BusinessException(SeckillServerCodeMsg.OPS_ERROR);
        }
        // 有效：设置这个random为无效，从Redis中移除，不用等到过期
        redisTemplate.delete(RedisKeys.RANDOM_IN_PATH.join(
                user.getId() + "", seckillId + ""));

        // 判断库存是否还有，没有就直接抛异常，不需要进行后面的判断了
        Boolean stockCountState = STOCK_COUNT_STATE_MAP.get(seckillId);
        if (stockCountState != null && !stockCountState) {
            // 库存已经 < 0 了 抛异常
            throw new BusinessException(SeckillServerCodeMsg.STOCKCOUNT_OVER_ERROR);
        }


        // 1. 获取秒杀商品对象，判断秒杀活动是否存在

        //SeckillGoodVO seckillGoodVO = seckillGoodService.findById(seckillId);
        SeckillGoodVO seckillGoodVO = seckillGoodService.findByCache(seckillId);
        if (seckillGoodVO == null) {
            throw new BusinessException(SeckillServerCodeMsg.OPS_ERROR);
        }
        // 2. 判断当前时间是否在秒杀活动的时间内
        Date now = new Date();  // 当前时间
        //   当前小于开始时间，活动未开始
        if (now.compareTo(seckillGoodVO.getStartDate()) < 0) {
            throw new BusinessException(SeckillServerCodeMsg.NOT_START_ERROR);
        }
        //   当前大于等于结束时间，活动已结束
        if (now.compareTo(seckillGoodVO.getEndDate()) >= 0) {
            throw new BusinessException(SeckillServerCodeMsg.ALREADY_OVER_ERROR);
        }

        // 3. 判断当前用户是不是重复下单了，不允许重复下单
        // 用Redis的set存下单成功用户，通过检查是否存在来判断是否重复下单
        Boolean isMember = redisTemplate.opsForSet().isMember(
                RedisKeys.SECKILL_SUCCESS_USER_SET.join(seckillId + ""), user.getId() + "");
        if (isMember != null && isMember) {
            throw new BusinessException(SeckillServerCodeMsg.ALREADY_AGEIN_ERROR);
        }


//        SeckillOrder seckillOrder = seckillOrderService.findBySeckillIdAndUserId(seckillId, user.getId());
//        if (seckillOrder != null) {
//            throw new BusinessException(SeckillServerCodeMsg.ALREADY_AGEIN_ERROR);
//        }
        // 4. 判断秒杀商品的库存是否足够
//        if (seckillGoodVO.getStockCount() <= 0){
//            throw new BusinessException(SeckillServerCodeMsg.STOCKCOUNT_OVER_ERROR);
//        }


        // Redis 库存预减操作
        Long stockCount = redisTemplate.opsForHash().increment(RedisKeys.SECKILL_GOOD_STOCKCOUNT_HASH.join(),
                seckillGoodVO.getId() + "",
                -1);
        // 预减结果小于 0
        // 表示库存已经不足，请求会被拦截
        if (stockCount < 0) {
            // 添加本地售完标记
            STOCK_COUNT_STATE_MAP.put(seckillId, false);
            throw new BusinessException(SeckillServerCodeMsg.STOCKCOUNT_OVER_ERROR);
        }


        // 5. 创建秒杀订单：
        //    包括  1.减库存  2.创建普通订单（秒杀订单依赖普通订单） 3.创建秒杀订单
        //String orderNo = orderInfoService.doSeckill(seckillId, user.getId());
        //return Result.success(orderNo);

        // 使用rocketMQ来优化上面的下单操作
        try {
            // 发送消息
            rocketMQTemplate.syncSend(MQConstents.SECKILL_ORDER_DEST,
                    new CreateOrderMsg(seckillId, user.getId(), uuid));
        } catch (Exception e) {
            // 如果发送消息失败了，需要设计重试机制
            e.printStackTrace();
        }
        return Result.success("正在抢购中，请耐心等待！");
    }
}
